// 版权所有2011 Go作者。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

// 包ecdsa实现椭圆曲线数字签名算法，如FIPS 186-4和第1节2.0版中定义的
// 一样。
// 
// 这个包生成的签名是不确定的，但熵是
// 与私钥和消息混合在一起，在随机源失败的情况下达到了相同级别的
// 安全性。
package ecdsa

// /[FIPS 186-4]参考ANSI X9。62-2005的大部分ECDSA算法。
// 该标准不是免费提供的，这在开源
// 实现中是一个问题，因为不仅实现者，而且任何维护者、
// 贡献者、审阅者、审核者和学习者都需要访问该标准。相反，这个
// 包引用并遵循等效的[SEC 1，Version 2.0]。
// 
// [FIPS 186-4]：https:
// www.secg.org/sec1-v2.pdf

import (
	"crypto"
	"crypto/aes"
	"crypto/cipher"
	"crypto/elliptic"
	"crypto/internal/randutil"
	"crypto/sha512"
	"errors"
	"io"
	"math/big"

	"golang.org/x/crypto/cryptobyte"
	"golang.org/x/crypto/cryptobyte/asn1"
)

// GF（N）中的可逆函数实现了快速逆。
type invertible interface {
	// Inverse返回k mod Params（）的倒数。N.
	Inverse(k *big.Int) *big.Int
}

// combinedMult实现快速组合乘法以进行验证。
type combinedMult interface {
	// CombinedMult返回[s1]G+[s2]P，其中G是生成器。
	CombinedMult(Px, Py *big.Int, s1, s2 []byte) (x, y *big.Int)
}

const (
	aesIV = "IV for ECDSA CTR"
)

// 公钥表示ECDSA公钥。
type PublicKey struct {
	elliptic.Curve
	X, Y *big.Int
}

// 在PublicKey上实现的任何方法可能也需要在
// PrivateKey上实现，因为后者嵌入了前者并将公开其方法。

// Equal报告pub和x是否具有相同的值。
// 
// 只有当两个关键点具有相同的曲线值时，才会认为它们具有相同的值。
// 请注意，例如椭圆。P256（）和椭圆。P256（）。Params（）是不同的
// 值，因为后者是通用的非恒定时间实现。
func (pub *PublicKey) Equal(x crypto.PublicKey) bool {
	xx, ok := x.(*PublicKey)
	if !ok {
		return false
	}
	return pub.X.Cmp(xx.X) == 0 && pub.Y.Cmp(xx.Y) == 0 &&
		// 标准库曲线实现是单例的，所以这个检查
		// 将适用于这些。其他曲线可能是等效的，即使不是
		// 单态，但没有确定的方法来检查这一点，而
		// 最好在安全方面出错。
		pub.Curve == xx.Curve
}

// PrivateKey表示ECDSA私钥。
type PrivateKey struct {
	PublicKey
	D *big.Int
}

// Public返回priv对应的公钥。
func (priv *PrivateKey) Public() crypto.PublicKey {
	return &priv.PublicKey
}

// Equal报告priv和x的值是否相同。
// 
// 参见公钥。关于如何比较曲线的详细信息，请参见Equal。
func (priv *PrivateKey) Equal(x crypto.PrivateKey) bool {
	xx, ok := x.(*PrivateKey)
	if !ok {
		return false
	}
	return priv.PublicKey.Equal(&xx.PublicKey) && priv.D.Cmp(xx.D) == 0
}

// 与priv签署签名摘要，阅读兰德的随机性。opts参数
// 目前未被使用，但与密码保持一致。签名者接口
// 应该是用于消化消息的哈希函数。
// 
// 此方法实现加密。Signer，这是一个支持密钥
// 的接口，其中私有部分保存在硬件模块中。常用的
// 用户可以直接使用该软件包中的SignASN1函数。
func (priv *PrivateKey) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) {
	r, s, err := Sign(rand, priv, digest)
	if err != nil {
		return nil, err
	}

	var b cryptobyte.Builder
	b.AddASN1(asn1.SEQUENCE, func(b *cryptobyte.Builder) {
		b.AddASN1BigInt(r)
		b.AddASN1BigInt(s)
	})
	return b.Bytes()
}

var one = new(big.Int).SetInt64(1)

// randFieldElement使用FIPS 186-4附录B.5.1中给出的程序返回给定
// 曲线顺序的随机元素。
func randFieldElement(c elliptic.Curve, rand io.Reader) (k *big.Int, err error) {
	params := c.Params()
	// 注意，对于P-521，这实际上比顺序多63位，因为
	// 除法舍入，但额外的位无关紧要。
	b := make([]byte, params.BitSize/8+8) // TODO:使用参数。N.BitLen（）
	_, err = io.ReadFull(rand, b)
	if err != nil {
		return
	}

	k = new(big.Int).SetBytes(b)
	n := new(big.Int).Sub(params.N, one)
	k.Mod(k, n)
	k.Add(k, one)
	return
}

// GenerateKey生成公钥和私钥对。
func GenerateKey(c elliptic.Curve, rand io.Reader) (*PrivateKey, error) {
	k, err := randFieldElement(c, rand)
	if err != nil {
		return nil, err
	}

	priv := new(PrivateKey)
	priv.PublicKey.Curve = c
	priv.D = k
	priv.PublicKey.X, priv.PublicKey.Y = c.ScalarBaseMult(k.Bytes())
	return priv, nil
}

// hashToInt将哈希值转换为整数。根据FIPS 186-4第6.4节
// 我们使用散列最左边的位来匹配曲线中
// 顺序的位长度。这也会执行第1节2.0版第4.1.3节的第5步。
func hashToInt(hash []byte, c elliptic.Curve) *big.Int {
	orderBits := c.Params().N.BitLen()
	orderBytes := (orderBits + 7) / 8
	if len(hash) > orderBytes {
		hash = hash[:orderBytes]
	}

	ret := new(big.Int).SetBytes(hash)
	excess := len(hash)*8 - orderBits
	if excess > 0 {
		ret.Rsh(ret, uint(excess))
	}
	return ret
}

// fermatiinverse使用Fermat的方法计算GF（P）中k的倒数
// （根据欧拉定理，幂模P-2）。这比欧几里得的方法（在
// 恒定时间属性，尽管math/big 
// math/big.Int.ModInverse和FIPS 186-4附录C.1中实现）具有更好的
// 本身并不是严格的恒定时间，所以它并不完美。
func fermatInverse(k, N *big.Int) *big.Int {
	two := big.NewInt(2)
	nMinus2 := new(big.Int).Sub(N, two)
	return new(big.Int).Exp(k, nMinus2, N)
}

var errZeroParam = errors.New("zero parameter")

// 签名对散列进行签名（这应该是对较大消息进行散列的结果）
// 使用私钥priv。如果散列长度大于
// 私钥的曲线顺序的位长度，则散列将被截断为该长度。它
// 将签名作为一对整数返回。大多数应用程序应该使用
// SignASN1，而不是直接处理r，s。
func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err error) {
	randutil.MaybeReadByte(rand)

	// 此实现从AES-CTR CSPRNG中派生nonce，其密钥为：
	// 
	// SHA2-512（priv.D | | | entropy | | hash）[32]
	// 
	// CSPRNG密钥与随机oracle无关，如
	// /[Coron]所示，在标准加密假设下，AES-CTR流与随机oracle 
	// 是不可区分的（参见[Larsson]中的示例）。
	// 
	// /[Coron]：https:
	// /[Larsson]：https:

	// /从兰德获得256位的熵。
	entropy := make([]byte, 32)
	_, err = io.ReadFull(rand, entropy)
	if err != nil {
		return
	}

	// 初始化SHA-512哈希上下文；消化
	md := sha512.New()
	md.Write(priv.D.Bytes()) // 私钥，
	md.Write(entropy)        // 熵，
	md.Write(hash)           // 和输入哈希；
	key := md.Sum(nil)[:32]  // 和compute ChopMD-256（SHA-512），
	// 这是一个不可区分的MAC。chuang jian defg

	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, nil, err
	}

	// 创建一个CSPRNG，用
	// 对AES-CTR实例的输出进行异或零流。
	csprng := cipher.StreamReader{
		R: zeroReader,
		S: cipher.NewCTR(block, []byte(aesIV)),
	}

	c := priv.PublicKey.Curve
	return sign(priv, &csprng, c, hash)
}

func signGeneric(priv *PrivateKey, csprng *cipher.StreamReader, c elliptic.Curve, hash []byte) (r, s *big.Int, err error) {
	// 2.0版第1节第4.1.3节
	N := c.Params().N
	if N.Sign() == 0 {
		return nil, nil, errZeroParam
	}
	var k, kInv *big.Int
	for {
		for {
			k, err = randFieldElement(c, *csprng)
			if err != nil {
				r = nil
				return
			}

			if in, ok := priv.Curve.(invertible); ok {
				kInv = in.Inverse(k)
			} else {
				kInv = fermatInverse(k, N) // N！=0 
			}

			r, _ = priv.Curve.ScalarBaseMult(k.Bytes())
			r.Mod(r, N)
			if r.Sign() != 0 {
				break
			}
		}

		e := hashToInt(hash, c)
		s = new(big.Int).Mul(priv.D, r)
		s.Add(s, e)
		s.Mul(s, kInv)
		s.Mod(s, N) // N！=0 
		if s.Sign() != 0 {
			break
		}
	}

	return
}

// SignASN1使用private key priv对散列（应该是对较大消息进行散列的结果）进行签名。如果散列长度大于
// 私钥的曲线顺序的位长度，则散列将被截断为该长度。它返回ASN。1.编码签名。
func SignASN1(rand io.Reader, priv *PrivateKey, hash []byte) ([]byte, error) {
	return priv.Sign(rand, hash, nil)
}

// Verify使用公钥pub验证散列r，s中的签名。它的
// 返回值记录签名是否有效。大多数应用程序都应该使用VerifyASN1，而不是直接处理r，s。
func Verify(pub *PublicKey, hash []byte, r, s *big.Int) bool {
	c := pub.Curve
	N := c.Params().N

	if r.Sign() <= 0 || s.Sign() <= 0 {
		return false
	}
	if r.Cmp(N) >= 0 || s.Cmp(N) >= 0 {
		return false
	}
	return verify(pub, c, hash, r, s)
}

func verifyGeneric(pub *PublicKey, c elliptic.Curve, hash []byte, r, s *big.Int) bool {
	// 第1节，2.0版，第4.1.4节
	e := hashToInt(hash, c)
	var w *big.Int
	N := c.Params().N
	if in, ok := c.(invertible); ok {
		w = in.Inverse(s)
	} else {
		w = new(big.Int).ModInverse(s, N)
	}

	u1 := e.Mul(e, w)
	u1.Mod(u1, N)
	u2 := w.Mul(r, w)
	u2.Mod(u2, N)

	// 检查是否实现了S1*g+S2*p 
	var x, y *big.Int
	if opt, ok := c.(combinedMult); ok {
		x, y = opt.CombinedMult(pub.X, pub.Y, u1.Bytes(), u2.Bytes())
	} else {
		x1, y1 := c.ScalarBaseMult(u1.Bytes())
		x2, y2 := c.ScalarMult(pub.X, pub.Y, u2.Bytes())
		x, y = c.Add(x1, y1, x2, y2)
	}

	if x.Sign() == 0 && y.Sign() == 0 {
		return false
	}
	x.Mod(x, N)
	return x.Cmp(r) == 0
}

// VerifyASN1验证ASN。1使用
// 公钥的哈希编码签名sig。其返回值记录签名是否有效。
func VerifyASN1(pub *PublicKey, hash, sig []byte) bool {
	var (
		r, s  = &big.Int{}, &big.Int{}
		inner cryptobyte.String
	)
	input := cryptobyte.String(sig)
	if !input.ReadASN1(&inner, asn1.SEQUENCE) ||
		!input.Empty() ||
		!inner.ReadASN1Integer(r) ||
		!inner.ReadASN1Integer(s) ||
		!inner.Empty() {
		return false
	}
	return Verify(pub, hash, r, s)
}

type zr struct {
	io.Reader
}

// Read将dst的内容替换为零。
func (z *zr) Read(dst []byte) (n int, err error) {
	for i := range dst {
		dst[i] = 0
	}
	return len(dst), nil
}

var zeroReader = &zr{}
